/**************************************************************************************************
THIS IS EMPTY HEADER
**************************************************************************************************/

/**************************************************************************************************
  Filename:       tuya_gen_service.c
  Revised:        
  Revision:       

  Description:    This file contains tuya general service profile
                  
**************************************************************************************************/

#include "bcomdef.h"
#include <stdio.h>
#include <string.h>
#include "OSAL.h"
#include "linkdb.h"
#include "att.h"
#include "gatt.h"
#include "gatt_uuid.h"
#include "gatt_profile_uuid.h"
#include "peripheral.h"
#include "gattservapp.h"
#include "clock.h"
#include "ota_app_service.h"
#include "log.h"
#include "error.h"
#include "ll.h"
#include "custom_tuya_ble_config.h"
#include "tuya_ble_log.h"
#include "tuya_gen_service.h"
#include "tuya_ble_type.h"
#include "tuya_ble_main.h"
#include "tuya_ble_api.h"

#define TUYA_GENERAL_SERVICE_UUID  0x1910
#define TUYA_NOTIFY_CHARACT_UUID   0x2B10
#define TUYA_WRITE_CHARACT_UUID    0x2B11

#define WRITE_HANDLE          6
#define NOTIFY_HANDLE         2

//tuya general service
CONST uint8 tuya_gen_ServiceUUID[ATT_BT_UUID_SIZE] =
{
    LO_UINT16(TUYA_GENERAL_SERVICE_UUID), HI_UINT16(TUYA_GENERAL_SERVICE_UUID)
};

//tuya Notify characteristic
CONST uint8 tuya_charNotiUUID[ATT_BT_UUID_SIZE] =
{
    LO_UINT16(TUYA_NOTIFY_CHARACT_UUID), HI_UINT16(TUYA_NOTIFY_CHARACT_UUID)
};

// tuya Write characteristic
CONST uint8 tuya_charWritUUID[ATT_BT_UUID_SIZE] =
{
    LO_UINT16(TUYA_WRITE_CHARACT_UUID), HI_UINT16(TUYA_WRITE_CHARACT_UUID)
};

static tuya_ProfileChangeCB_t tuya_AppCBs = NULL;

//tuya general service
static CONST gattAttrType_t tuya_gen_Service = {ATT_BT_UUID_SIZE, tuya_gen_ServiceUUID};
//tuya Notify characteristic
static uint8 tuya_charNotiProps = GATT_PROP_READ | GATT_PROP_NOTIFY;
static uint8 tuya_charNotiValue[TUYA_BLE_DATA_MTU_MAX] = {0}; // tbd
static uint8 tuya_charNotiDescipt[] = "Dev->APP\0"; // tbd

// tuya Write characteristic
static uint8 tuya_charWritProps = GATT_PROP_READ | GATT_PROP_WRITE | GATT_PROP_WRITE_NO_RSP;
static uint8 tuya_charWritValue[TUYA_BLE_DATA_MTU_MAX] = {0}; // tbd
static uint8 tuya_charWritDescipt[] = "APP->Dev\0"; // tbd

static gattCharCfg_t tuya_ResponseCCCD;

tuya_Srv_Evt_t evt = {0};
tuya_ble_evt_param_t event = {0};

static gattAttribute_t tuya_gen_AttrTbl[] =
{
    //tuya general Service
    {
        {ATT_BT_UUID_SIZE, primaryServiceUUID}, /* type */
        GATT_PERMIT_READ,                       /* permissions */
        0,                                      /* handle */
        (uint8*)& tuya_gen_Service              /* pValue */
    },

    //tuya Notify characteristic Declaration
    {
        {ATT_BT_UUID_SIZE, characterUUID},
        GATT_PERMIT_READ,
        0,
        &tuya_charNotiProps
    },

    //tuya Notify characteristic Value
    {
        {ATT_BT_UUID_SIZE, tuya_charNotiUUID},
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        (uint8 *)&tuya_charNotiValue
    },

    //tuya Notify characteristic Description
    {
        {ATT_BT_UUID_SIZE, charUserDescUUID},
        GATT_PERMIT_READ,
        0,
        tuya_charNotiDescipt
    },


    // tuya response Client Characteristic Configuration
    {
        {ATT_BT_UUID_SIZE, clientCharCfgUUID},
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        (uint8 *)&tuya_ResponseCCCD
    },

    // tuya Write characteristic Declaration
    {
        {ATT_BT_UUID_SIZE, characterUUID},
        GATT_PERMIT_READ,
        0,
        &tuya_charWritProps
    },

    //tuya Write characteristic Value
    {
        {ATT_BT_UUID_SIZE, tuya_charWritUUID},
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        (uint8 *)&tuya_charWritValue
    },

		//tuya Write characteristic Description
    {
        {ATT_BT_UUID_SIZE, charUserDescUUID},
        GATT_PERMIT_READ,
        0,
        tuya_charWritDescipt
    },


};

/*********************************************************************
    @fn          tuya_gen_ConnStatusCB

    @brief       Simple Profile link status change handler function.

    @param       connHandle - connection handle
    @param       changeType - type of change

    @return      none
*/
static void tuya_gen_ConnStatusCB ( uint16 connHandle, uint8 changeType )
{
    // Make sure this is not loopback connection
	  //TUYA_APP_LOG_INFO("ConnStatusCB ");
    if ( connHandle != LOOPBACK_CONNHANDLE )
    {
        // Reset Client Char Config if connection has dropped
        if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED )      ||
                ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) &&
                  ( !linkDB_Up( connHandle ) ) ) )
        {
            tuya_ResponseCCCD.value = 0;
					  TUYA_APP_LOG_INFO("ConnStatusCB dis_con ");
        }
				else
				{
					  TUYA_APP_LOG_INFO("ConnStatusCB con ");
				}
    }
}

uint8 tuya_NotifyIsReady(void)
{
    return (tuya_ResponseCCCD.value == GATT_CLIENT_CFG_NOTIFY);
}

void set_tuya_Notify(void)
{
    tuya_ResponseCCCD.value |= GATT_CLIENT_CFG_NOTIFY;
}

void clear_tuya_Notify(void)
{
    tuya_ResponseCCCD.value = 0;
}
/*********************************************************************
    @fn          tuya_gen_Notify

    @brief       Send a notification containing a bloodPressure
                measurement.

    @param       connHandle - connection handle
    @param       pNoti - pointer to notification structure

    @return      Success or Failure
*/
bStatus_t tuya_gen_Notify( uint16 connHandle, attHandleValueNoti_t* pNoti)
{
    uint16 value = tuya_ResponseCCCD.value;

    // If notifications enabled
    if ( value & GATT_CLIENT_CFG_NOTIFY )
    {
        // Set the handle
        pNoti->handle = tuya_gen_AttrTbl[NOTIFY_HANDLE].handle;
			  //LOG("W_Nt:[P_hdl] %x\n", pNoti->handle);
        // Send the Indication
        return GATT_Notification( connHandle, pNoti, FALSE);
    }

    return bleIncorrectMode;
}

/*********************************************************************
    @fn      tuya_gen_WriteAttrCB

    @brief   Validate attribute data prior to a write operation

    @param   connHandle - connection message was received on
    @param   pAttr - pointer to attribute
    @param   pValue - pointer to data to be written
    @param   len - length of data
    @param   offset - offset of the first octet to be written

    @return  Success or Failure
*/

static bStatus_t tuya_gen_WriteAttrCB( uint16 connHandle, gattAttribute_t* pAttr,
                                      uint8* pValue, uint8 len, uint16 offset )
{
    bStatus_t status = SUCCESS;

    //LOG("W_CB:[C_hdl] %x\n", connHandle);
    // If attribute permissions require authorization to write, return error
    if ( gattPermitAuthorWrite( pAttr->permissions ) )
    {
        // Insufficient authorization
        return ( ATT_ERR_INSUFFICIENT_AUTHOR );
    }

    if ( osal_memcmp(pAttr->type.uuid, clientCharCfgUUID, 2) )
    {
        // case:GATT_CLIENT_CHAR_CFG_UUID(0x2902)
        status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
                                                 offset, GATT_CLIENT_CFG_NOTIFY );

        if ( status == SUCCESS && tuya_AppCBs)
        {
            uint16 charCfg = BUILD_UINT16( pValue[0], pValue[1] );
            LOG("CCCD set: [%d]\n", charCfg);
            evt.ev = (charCfg == GATT_CFG_NO_OPERATION)?TUYA_EVT_NOTI_DISABLED:TUYA_EVT_NOTI_ENABLED;
            tuya_AppCBs(&evt);
        }
    }
    else if(osal_memcmp(pAttr->type.uuid, tuya_charWritUUID, 2) && \
            (pAttr->handle == tuya_gen_AttrTbl[WRITE_HANDLE].handle))  // to be added.
    {
        if(tuya_AppCBs)
        {
////						TUYA_APP_LOG_INFO("write cb...");
////						TUYA_BLE_HEXDUMP((uint8_t *)&connHandle, 2);
//					  LOG("\n offset:[%x]--", offset);
//					
//						TUYA_BLE_HEXDUMP(evt.data, len);
					
            evt.ev = TUYA_EVT_BLE_DATA_RECIEVED;
            evt.len = (uint8_t)len;
            evt.data = pValue;
            tuya_AppCBs(&evt);
        }
    }
    else
    {
        return ( ATT_ERR_ATTR_NOT_FOUND );
    }

    return ( status );
}

/*********************************************************************
    @fn          tuya_gen_ReadAttrCB

    @brief       Read an attribute.

    @param       connHandle - connection message was received on
    @param       pAttr - pointer to attribute
    @param       pValue - pointer to data to be read
    @param       pLen - length of data to be read
    @param       offset - offset of the first octet to be read
    @param       maxLen - maximum length of data to be read

    @return      Success or Failure
*/
static uint8 tuya_gen_ReadAttrCB( uint16 connHandle, gattAttribute_t* pAttr,
                                 uint8* pValue, uint8* pLen, uint16 offset, uint8 maxLen )
{
    bStatus_t status = SUCCESS;
//    uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
	  //LOG("[rcb]offset: [%x],\n", offset);

    // If attribute permissions require authorization to read, return error
    if ( gattPermitAuthorRead( pAttr->permissions ) )
    {
        // Insufficient authorization
        return ( ATT_ERR_INSUFFICIENT_AUTHOR );
    }

    if ( osal_memcmp(pAttr->type.uuid, clientCharCfgUUID, 2)) //clientCharCfgUUID
    {
        // case:GATT_CLIENT_CHAR_CFG_UUID(0x2902)
        //*pLen = 2;
        //osal_memcpy(pValue, pAttr->pValue, 2);
			  *pLen = sizeof(gattCharCfg_t);
			  osal_memcpy(pValue, (uint8 *)&tuya_ResponseCCCD, *pLen);
    }
    else if(osal_memcmp(pAttr->type.uuid, tuya_charNotiUUID, 2))
    {
        //*pLen = 1;
        //pValue[0] = bleuart_PTCharValue[0]; // TBD. ??
        //pValue[0] = '1';
        *pLen = TUYA_BLE_DATA_MTU_MAX;

        for(int i=0; i<TUYA_BLE_DATA_MTU_MAX; i++)
            pValue[i] = tuya_charNotiValue[i];
    }
    else
    {
        return ( ATT_ERR_ATTR_NOT_FOUND );
    }
		

    return ( status );
}

/*********************************************************************
    PROFILE CALLBACKS
*/
// Device Info Service Callbacks
CONST gattServiceCBs_t tuya_gen_ProfileCBs =
{
    tuya_gen_ReadAttrCB,       // Read callback function pointer
    tuya_gen_WriteAttrCB,      // Write callback function pointer
    NULL                       // Authorization callback function pointer
};

/*********************************************************************
    PUBLIC FUNCTIONS
*/

/*********************************************************************
    @fn      tuya_gen_AddService

    @brief   Initializes the Simple Profile service by registering
            GATT attributes with the GATT server.

    @param   services - services to add. This is a bit map and can
                       contain more than one service.

    @return  Success or Failure
*/
bStatus_t tuya_gen_AddService( tuya_ProfileChangeCB_t cb) // add later when necessary.
//bStatus_t tuya_gen_AddService( void)
{
    uint8 status = SUCCESS;
	  TUYA_APP_LOG_INFO("add tuya service ...");
    // Register with Link DB to receive link status change callback
    VOID linkDB_Register( tuya_gen_ConnStatusCB );
    tuya_ResponseCCCD.connHandle = INVALID_CONNHANDLE;
    tuya_ResponseCCCD.value = 0;
    // Register GATT attribute list and CBs with GATT Server App
    status = GATTServApp_RegisterService( tuya_gen_AttrTbl,
                                          GATT_NUM_ATTRS( tuya_gen_AttrTbl ),
                                          &tuya_gen_ProfileCBs );

    if(status!=SUCCESS)
			  TUYA_APP_LOG_INFO("fail to add tuya service ");
    tuya_AppCBs = cb;
    return ( status );
}

void tuya_gen_ServiceEvt(tuya_Srv_Evt_t* pev)
{
    switch(pev->ev)
    {
				case TUYA_EVT_NOTI_DISABLED:
						clear_tuya_Notify();
						break;

				case TUYA_EVT_NOTI_ENABLED :
						set_tuya_Notify();
						break;

				case TUYA_EVT_BLE_DATA_RECIEVED:
				{
						//TUYA_APP_LOG_INFO("D_REC ...");
						//TUYA_BLE_HEXDUMP((uint8_t*)pev->data, (uint8_t)pev->len);
					  //LOG("B_D_R]\n");
//						uint8_t evt_type = pev->data[0];
						//tuya_ble_evt_param_t event;
						//switch(evt_type)
						event.hdr.event = TUYA_BLE_EVT_MTU_DATA_RECEIVE;
						if(pev->len > 20)
						{
								event.mtu_data.len = 20;
						}
						else
						{
								event.mtu_data.len = pev->len;
						}
						VOID osal_memcpy(event.mtu_data.data, (uint8_t*)pev->data, event.mtu_data.len);
						tuya_ble_event_send(&event);
						tuya_ble_main_tasks_exec();
				}
				    break;

				default:
						break;
    }
}

